/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.inspur.edp.bff.designtime.generator.filter;

import com.inspur.edp.bef.bizentity.GspBizEntityObject;
import com.inspur.edp.bef.bizentity.GspBusinessEntity;
import com.inspur.edp.bff.designtime.generator.DotNetToJavaStringHelper;
import com.inspur.edp.bff.designtime.generator.VmGeneratorContext;
import com.inspur.edp.caf.generator.baseInfo.ParameterInfo;
import com.inspur.edp.caf.generator.baseInfo.TypeInfo;
import com.inspur.edp.caf.generator.method.ClassMethodGenerator;
import com.inspur.edp.caf.generator.utils.ExpressionUtils;
import com.inspur.edp.cef.designtime.api.IGspCommonField;
import com.inspur.edp.cef.designtime.api.element.GspAssociation;
import com.inspur.edp.cef.spi.jsonser.base.StringUtils;
import com.inspur.edp.das.commonmodel.IGspCommonElement;
import com.inspur.edp.das.commonmodel.IGspCommonObject;
import com.inspur.edp.formserver.viewmodel.GspViewModelElement;
import com.inspur.edp.formserver.viewmodel.GspViewObject;
import com.inspur.edp.udt.designtime.api.entity.ComplexDataTypeDef;
import com.inspur.edp.udt.designtime.api.entity.SimpleDataTypeDef;
import com.inspur.edp.udt.designtime.api.entity.UnifiedDataTypeDef;
import com.inspur.edp.udt.designtime.api.entity.dbInfo.ColumnMapType;
import lombok.var;
import java.util.List;
import org.eclipse.jdt.core.dom.*;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;

public class TransFilterMethodGenerator extends ClassMethodGenerator {

    private static final String ParamFieldName = "fieldName";
    private GspViewObject vmObject;
    private GspBizEntityObject beObject;
    private VmGeneratorContext vmGeneratorContext;
    //todo 多值udt会有重复的，增加判断 先让代码编译过去
    private List<String> udtCaseLabels = new ArrayList<>();

    // string TransFilterField(string fieldName);
    public TransFilterMethodGenerator(GspViewObject vmObject, GspBizEntityObject beObject,
                                      VmGeneratorContext vmGeneratorContext) {
        this.vmObject = vmObject;
        this.beObject = beObject;
        this.vmGeneratorContext = vmGeneratorContext;
    }


    @Override
    protected String getMethodName() {
        return "transFilterField";
    }

    @Override
    protected TypeInfo getReturnType() {
        return new TypeInfo("String");
    }

    @Override
    protected ArrayList<Modifier.ModifierKeyword> getAccessModifiers() {
        return new ArrayList<Modifier.ModifierKeyword>(
                Arrays.asList(new Modifier.ModifierKeyword[]{Modifier.ModifierKeyword.PUBLIC_KEYWORD}));
    }

    @Override
    protected ArrayList<ParameterInfo> getParameterCollection() {
        ParameterInfo paramInfo = new ParameterInfo();
        paramInfo.setParamType(new TypeInfo("String"));
        paramInfo.setParamName(ParamFieldName);
        return new ArrayList(java.util.Arrays.asList(new ParameterInfo[]{paramInfo}));
    }

    @Override
    protected Block buildMethodBody() {
        var block = ast.newBlock();
        ReturnStatement returnStatement = getDefaultReturnStatement();
        if (vmObject.getIsVirtual()) {
            block.statements().add(returnStatement);
            return block;
        }

        ArrayList<Statement> sections = new ArrayList<Statement>();

        for (var voElement : vmObject.getAllElementList(false)) {
            if (voElement.getIsVirtual()) {
                continue;
            }
            if (voElement.getIsUdt()) {
                var caseStatements = buildSection(voElement, voElement.getLabelID(), "");
                sections.addAll(caseStatements);
                continue;
            }

            if (voElement.getHasAssociation()) {

                var caseStatements = buildAssoSection(voElement, voElement.getLabelID(), "");
                sections.addAll(caseStatements);
            }

        }

        SwitchStatement switchStatement = ast.newSwitchStatement();
        switchStatement.setExpression(ast.newSimpleName(ParamFieldName));
        switchStatement.statements().addAll(sections);
        if(switchStatement.statements().size()>0)
        block.statements().add(switchStatement);
        block.statements().add(returnStatement);

        return block;
    }

    private ArrayList<Statement> buildSection(IGspCommonField voElement, String parentLabel, String returnValue) {

        ArrayList<Statement> statements = new ArrayList<>();
        var udt = vmGeneratorContext.<UnifiedDataTypeDef>getMetadataContent(voElement.getUdtPkgName(), voElement.getUdtID());
        if (udt != null && udt instanceof SimpleDataTypeDef) {
            var sections = buildUdtSection(voElement,
                    (SimpleDataTypeDef) ((udt instanceof SimpleDataTypeDef) ? udt : null), parentLabel,
                    returnValue);
            statements.addAll(sections);
        } else {
            var sections = buildUdtSection(voElement,
                    (ComplexDataTypeDef) ((udt instanceof ComplexDataTypeDef) ? udt : null));
            if(sections != null)
                statements.addAll(sections);
        }
        return statements;
    }

    private ArrayList<Statement> buildNormalSection(String label, String returnValue) {
        java.util.ArrayList<Statement> labels = new java.util.ArrayList<Statement>();
        labels.add(getCaseSwitchLabel(label));
        if (!label.equals(StringUtils.toCamelCase(label))) {
            labels.add(getCaseSwitchLabel(StringUtils.toCamelCase(label)));
        }
        labels.add(getCaseReturnStatement(returnValue));
        return labels;
    }

    private ArrayList<Statement> buildUdtSection(IGspCommonField voElement, SimpleDataTypeDef udt, String parentLabel,
                                 String returnValue) {

        var udtLabel = parentLabel + "." + udt.getCode();
        String udtLabelTemp=udtLabel+"."+udt.getCode();
        ArrayList<Statement> labels = new ArrayList<Statement>();
        labels.add(getCaseSwitchLabel(udtLabelTemp));
        if(udtLabelTemp!=StringUtils.toCamelCase(udtLabelTemp)){
            labels.add(getCaseSwitchLabel(StringUtils.toCamelCase(udtLabelTemp)));
        }
        labels.add(getCaseSwitchLabel(udtLabel));
        if (udtLabel != StringUtils.toCamelCase(udtLabel)) {
            labels.add(getCaseSwitchLabel(StringUtils.toCamelCase(udtLabel)));
        }
        labels.add(getCaseSwitchLabel(voElement.getLabelID()));
        if (voElement.getLabelID().equals( StringUtils.toCamelCase(voElement.getLabelID()))==false) {
            labels.add(getCaseSwitchLabel(StringUtils.toCamelCase(voElement.getLabelID())));
        }

        IGspCommonField beElement = getBeElement(
                (GspViewModelElement) ((voElement instanceof GspViewModelElement) ? voElement : null));
        if (beElement == null) {
            throw new RuntimeException("没有找到字段" + voElement.getLabelID() + "的映射字段");
        }

        var value =
                DotNetToJavaStringHelper.isNullOrEmpty(returnValue) ? beElement.getLabelID() : returnValue;

        labels.add(getCaseReturnStatement(value));


        if (voElement.getHasAssociation()) {
            var asso = udt.getChildAssociations().get(0);
            var assoSections = buildUdtAssoSection(voElement, udtLabel, returnValue, asso);
            labels.addAll(assoSections);
        }
        return labels;
    }

    private ArrayList<Statement> buildAssoSection(IGspCommonField voElement, String parentLabel, String returnValue) {

        var asso = voElement.getChildAssociations().get(0);
        ArrayList<Statement> statements = new ArrayList<>();
        statements.addAll(buildNormalSection(parentLabel+"."+parentLabel,parentLabel));
        for (var refElement : asso.getRefElementCollection()) {
            IGspCommonField beElement = getBeElement(
                    (GspViewModelElement) ((refElement instanceof GspViewModelElement) ? refElement : null));

            var childValue =
                    DotNetToJavaStringHelper.isNullOrEmpty(returnValue) ? beElement.getLabelID() : returnValue;

            var childLabel =
                    DotNetToJavaStringHelper.isNullOrEmpty(parentLabel) ? refElement.getLabelID() : parentLabel;
            childLabel = childLabel + "." + refElement.getLabelID();
            if (refElement.getIsUdt()) {
                statements.addAll(buildSection(refElement, childLabel, childValue));
            } else {
                statements.addAll(buildNormalSection(childLabel, childValue));
            }

        }
        return statements;
    }

    private ArrayList<Statement> buildUdtAssoSection(IGspCommonField voElement, String parentLabel,
                                     String returnValue, GspAssociation udtAsso) {

        ArrayList<Statement> sections = new ArrayList<>();
        var asso = voElement.getChildAssociations().get(0);
        for (var refElement : asso.getRefElementCollection()) {
            IGspCommonField beElement = getBeElement(
                    (GspViewModelElement) ((refElement instanceof GspViewModelElement) ? refElement : null));
            if(beElement == null)
                throw new RuntimeException(((GspViewObject)voElement.getBelongObject()).getBelongModel().getCode()+
                        "中，关联字段"+voElement.getLabelID()+"的关联带出字段"+refElement.getLabelID()
                        +"对应的be带出字段不存在");
            IGspCommonField udtRefElement = getUdtRefElement(refElement, udtAsso);
            var childValue =
                    DotNetToJavaStringHelper.isNullOrEmpty(returnValue) ? beElement.getLabelID() : returnValue;

            var childLabel =
                    DotNetToJavaStringHelper.isNullOrEmpty(parentLabel) ? refElement.getLabelID() : parentLabel;
            if (udtRefElement != null) {
                childLabel = childLabel + "." + udtRefElement.getLabelID();
            } else {
                childLabel = childLabel + "." + refElement.getLabelID();
            }
            if (beElement.getIsUdt()) {
                //todo 关联带出多值udt后续处理
                try {
                    var udt = vmGeneratorContext.<UnifiedDataTypeDef>getMetadataContent(voElement.getUdtPkgName(), beElement.getUdtID());
                    if (udt != null && udt instanceof ComplexDataTypeDef) {
                        continue;
                    }
                }
                catch (Exception ex){
                    throw new RuntimeException("获取" + refElement.getLabelID() +" 对应的" + refElement.getUdtID() + "udt元数据失败");
                }
                sections.addAll(buildSection(refElement, childLabel, childValue));
            } else {
                sections.addAll(buildNormalSection(childLabel, childValue));
            }

        }

        return sections;
    }

    private IGspCommonField getUdtRefElement(IGspCommonField refElement, GspAssociation udtAsso) {

        for (IGspCommonField udtRefElement : udtAsso.getRefElementCollection()) {
            if (udtRefElement.getRefElementId() .equals( refElement.getRefElementId())) {
                return udtRefElement;
            }
        }

        return null;
    }

    //private void buildAssoSection(IGspCommonField voElement, string parentLabel, string returnValue, ref List<SwitchSectionSyntax> sections)
    //{
    //    var asso = voElement.ChildAssociations[0];
    //    foreach (var refElement in asso.RefElementCollection)
    //    {
    //        var beElement = GetBeElement(refElement as GspViewModelElement);
    //        var childValue = string.IsNullOrEmpty(returnValue) ? beElement.LabelID : returnValue;
    //        var childLabel = string.IsNullOrEmpty(parentLabel) ? refElement.LabelID : parentLabel + refElement.LabelID;
    //        if(refElement.IsUdt)
    //            BuildSection(refElement, childLabel, childValue, ref sections);

    //    }
    //}

    private IGspCommonField getBeElement(GspViewModelElement voElement) {

        var objId = voElement.getMapping().getTargetObjectId();
        if(objId == null || "".equals(objId))
            throw new RuntimeException("字段"+voElement.getCode()+"上没有记录映射对象Id，请打开vo设计器重新添加字段");
        if (objId .equals( beObject.getID())) {
            return getBeElemet(voElement, beObject);
        }


        var be = vmGeneratorContext.<GspBusinessEntity>getMetadataContent(
                voElement.getMapping().getTargetMetadataPkgName(), voElement.getMapping().getTargetMetadataId());

        var beObj = be.findObjectById(objId);
        if(beObj == null)
            throw new RuntimeException("be元数据"+be.getCode()+"上没有找到对象"+objId+"，对应vo对象为"+voElement.getBelongObject().getCode());
        return getBeElemet(voElement, beObj);
    }

    private IGspCommonField getBeElemet(GspViewModelElement voElement, IGspCommonObject beObj) {
        if (!voElement.getIsRefElement()) {
            return beObj.findElement(voElement.getMapping().getTargetElementId());
        }


        for (IGspCommonElement beEle : beObj.getAllElementList(true)) {
            if (beEle.getID().equals(voElement.getMapping().getTargetElementId())) {
                return beEle;
            }
        }

        return null;
    }

    private SwitchCase getCaseSwitchLabel(String caseValue) {
        var caseLabel = ast.newSwitchCase();
        caseLabel.setExpression(ExpressionUtils.getStringLiteral(ast,caseValue));
        return caseLabel;
    }

    private ReturnStatement getCaseReturnStatement(String value) {
        ReturnStatement returnStatement = ast.newReturnStatement();
        returnStatement.setExpression(ExpressionUtils.getStringLiteral(ast, value));
        return returnStatement;
    }

    private ArrayList<Statement> buildUdtSection(IGspCommonField voElement, ComplexDataTypeDef udt) {
        if (udt.getDbInfo().getMappingType() == ColumnMapType.SingleColumn) {
            return null;
        }
        IGspCommonField beElement = getBeElement(
                (GspViewModelElement) ((voElement instanceof GspViewModelElement) ? voElement : null));
        if (beElement == null) {
            throw new RuntimeException("没有找到字段" + voElement.getLabelID() + "的映射字段");
        }

        ArrayList<Statement> statements = new ArrayList<>();
        for (var mapping : beElement.getMappingRelation()) {

            var beChildEleId = mapping.getKeyInfo();

            var udtEleId = mapping.getValueInfo();
            IGspCommonField beChildElement = getBeChildElement(beElement, beChildEleId);

            var udtElement = udt.findElement(udtEleId);
            if (beChildElement == null || udtElement == null) {
                throw new RuntimeException(
                        "Vo" + vmObject.getBelongModel().getCode() + "编译失败，多值UDT"
                                + udt.getCode() + "数据库映射有异常");
            }
            statements.addAll(buildUdtElementSection(voElement, udtElement, beChildElement, udt));
        }

        return statements;
    }

    private IGspCommonField getBeChildElement(IGspCommonField beElement, String childId) {

        for (var beChildElement : beElement.getChildElements()) {
            if (childId.equals(beChildElement.getID())) {
                return beChildElement;
            }
        }

        return null;
    }

    private ArrayList<Statement> buildUdtElementSection(IGspCommonField voElement, IGspCommonField udtElement,
                                        IGspCommonField beElement, ComplexDataTypeDef udt) {

        var udtLabel = voElement.getLabelID() + "." + udtElement.getLabelID();
        ArrayList<Statement> labels = new ArrayList<Statement>();
        if(!udtCaseLabels.contains(udtLabel)){
            labels.add(getCaseSwitchLabel(udtLabel));
            udtCaseLabels.add(udtLabel);
        }
        if (udtLabel .equals( StringUtils.toCamelCase(udtLabel))==false) {
            if(!udtCaseLabels.contains(StringUtils.toCamelCase(udtLabel))){
                labels.add(getCaseSwitchLabel(StringUtils.toCamelCase(udtLabel)));
                udtCaseLabels.add(StringUtils.toCamelCase(udtLabel));
            }
        }
        if(!udtCaseLabels.contains(beElement.getLabelID())){
            labels.add(getCaseSwitchLabel(beElement.getLabelID()));
            udtCaseLabels.add(beElement.getLabelID());
        }

        if (beElement.getLabelID().equals(StringUtils.toCamelCase(beElement.getLabelID()))==false) {
            if(!udtCaseLabels.contains(StringUtils.toCamelCase(beElement.getLabelID()))){
                labels.add(getCaseSwitchLabel(StringUtils.toCamelCase(beElement.getLabelID())));
                udtCaseLabels.add(StringUtils.toCamelCase(beElement.getLabelID()));
            }

        }
        labels.add(getCaseReturnStatement(beElement.getLabelID()));

        return labels;
    }

    private ReturnStatement getDefaultReturnStatement() {
        ReturnStatement returnStatement = ast.newReturnStatement();
        returnStatement.setExpression(ast.newSimpleName(ParamFieldName));
        return returnStatement;
    }
}
